﻿
package extremefx.i18n {
	import extremefx.DateTime;
	import extremefx.i18n.Calendar;	

	/**
	 * @author Marcelo Volmaro
	 * <p>This is the Julian calendar.</p>
	 * <p>The Julian calendar supports only the Common Era from January 1, 1 (Gregorian) to December 31, 9999 (Gregorian).</p>
	 */
	public class JulianCalendar extends Calendar {
		private static const JULIAN_ERA:uint = 1;
		private static const MIN:DateTime = new DateTime (1, 1, 1, 0, 0, 0);
		private static const MAX:DateTime = new DateTime (9999, 12, 31, 11, 59, 59);
		
		public function JulianCalendar() {
			super();
			_abbrEraNames = ["C.E."];
			_eraNames = ["Common Era"];
			if (_twoDigitYearMax == 99) _twoDigitYearMax = 2029;
		}
		
		public override function get eras():Array {
			return [JULIAN_ERA];  
		}
		
		public override function set twoDigitYearMax(pYear:int):void{ 
			_argumentInRange ("value", pYear, 100, _getMaxYear());
			_twoDigitYearMax = pYear;
		}
		
		private function _checkEra(pEra:Array):void {
			var era:int = int(pEra[0]);
			if (era == -1) era = JULIAN_ERA;
			if (era != JULIAN_ERA) throw new ArgumentError("Era value was not valid.");
			pEra[0] = era;
		}
		
		protected override function _checkYE(pYear:int, pEra:Array):void {
			_checkEra(pEra);
			_argumentInRange("pYear", pYear, 1, 9999);
		}
		
		private function _checkYME(pYear:int, pMonth:int, pEra:Array):void {
			_checkYE(pYear, pEra);
			if (pMonth < 1 || pMonth > 12) throw new RangeError("pMonth", "Month must be between one and twelve.");
		}
		
		private function _checkYMDE(pYear:int, pMonth:int, pDay:int, pEra:Array):void {
			_checkYME(pYear, pMonth, pEra);
			_argumentInRange("pDay", pDay, 1, getDaysInMonth(pYear, pMonth, int(pEra[0])));
			if (pYear == 9999 && ((pMonth == 10 && pDay > 19) || pMonth > 10)) throw new RangeError("The maximum Julian date is 19. 10. 9999.");
		}
		
		public override function addMonths(pTime:DateTime, pMonths:int):DateTime {
			var rd:int = CCFixed._fromDateTime(pTime);
			var out:Array = CCJulian._dmyFromFixed(rd);
			
			var remainder:Array = [];
			var month:int = pMonths + int(out[1]);
			var year:int = CCMath.divMod(remainder, month, 12);
			month = int(remainder[0]);
			rd = CCJulian._fixedFromDMY(int(out[2]), month, year);
			var t:DateTime = CCFixed._toDateTime(rd);
			t.add(pTime.timeOfDay); 
			return t;
		}
		
		public override function addYears(pTime:DateTime, pYears:int):DateTime {
			var rd:int = CCFixed._fromDateTime(pTime);
			var out:Array = CCJulian._dmyFromFixed(rd);
			
			pYears += int(out[0]);
			rd = CCJulian._fixedFromDMY(int(out[2]), int(out[1]), pYears);
			var t:DateTime = CCFixed._toDateTime(rd);
			t.add(pTime.timeOfDay); 
			return t;
		}
		
		public override function getDayOfMonth(pTime:DateTime):int {
			var rd:int = CCFixed._fromDateTime(pTime);
			return CCJulian._dayFromFixed(rd);
		}
		
		public override function getDayOfWeek(pTime:DateTime):int {
			var rd:int = CCFixed._fromDateTime(pTime);
			return CCFixed._dayOfWeek(rd);
		}
		
		public override function getDayOfYear(pTime:DateTime):int {
			var rd:int = CCFixed._fromDateTime(pTime);
			var year:int = CCJulian._yearFromFixed(rd);
			var rd1_1:int = CCJulian._fixedFromDMY(1, 1, year);
			return rd - rd1_1 + 1;
		}
		
		public override function getDaysInMonth(pYear:int, pMonth:int, pEra:int = -1):int {
			_checkYME(pYear, pMonth, [pEra]);
			var rd1:int = CCJulian._fixedFromDMY(1, pMonth, pYear);
			var rd2:int = CCJulian._fixedFromDMY(1, pMonth+1, pYear);
			return rd2 - rd1;
		}
		
		public override function getDaysInYear(pYear:int, pEra:int = -1):int {
			_checkYE(pYear, [pEra]);
			var rd1:int = CCJulian._fixedFromDMY(1, 1, pYear);
			var rd2:int = CCJulian._fixedFromDMY(1, 1, pYear+1);
			return rd2 - rd1;
		}
		
		public override function getEra(pTime:DateTime):int {
			return JULIAN_ERA;
		}
		
		public override function getLeapMonth(pYear:int, pEra:int = -1):int {
			return 0;
		}
		
		public override function getMonth(pTime:DateTime):int {
			var rd:int = CCFixed._fromDateTime(pTime);
			return CCJulian._monthFromFixed(rd);
		}
		
		public override function getMonthsInYear(pYear:int, pEra:int = -1):int {
			_checkYE(pYear, [pEra]);
			return 12;
		}
		
		public override function getYear(pTime:DateTime):int {
			var rd:int = CCFixed._fromDateTime(pTime);
			return CCJulian._yearFromFixed(rd);
		}
		
		public override function isLeapDay(pYear:int, pMonth:int, pDay:int, pEra:int= -1):Boolean {
			_checkYMDE(pYear, pMonth, pDay, [pEra]);
			return isLeapYear(pYear) && pMonth == 2 && pDay == 29;
		}
		
		public override function isLeapMonth(pYear:int, pMonth:int, pEra:int= -1):Boolean {
			_checkYME(pYear, pMonth, [pEra]);
			return false;
		}
		
		public override function isLeapYear(pYear:int, pEra:int= -1):Boolean {
			_checkYE(pYear, [pEra]);
			return CCJulian._isLeapYear(pYear);
		}
		
		public override function toDateTime(pYear:int, pMonth:int, pDay:int, pHour:int, pMinute:int, pSecond:int, pMSecond:int, pEra:int = -1):DateTime {
			_checkYMDE(pYear, pMonth, pDay, [pEra]);
			_checkHMSM(pHour, pMinute, pSecond, pMSecond);
			var rd:int = CCJulian._fixedFromDMY(pDay, pMonth, pYear);
			return CCFixed._toDateTime2(rd, pHour, pMinute, pSecond, pMSecond);
		}
		
		public override function get algorithmType():uint {
			return CalendarAlgorithmType.SOLAR;
		}
		
		public override function get minSupportedDateTime():DateTime {
			return MIN;
		}
		
		public override function get maxSupportedDateTime():DateTime {
			return MAX;
		}
	}
}
